import { createApp, createVNode } from 'vue'
import App from './App.vue'
import router from './router'
import { store, key } from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 将组件样式修改为中文提示
import locale from 'element-plus/lib/locale/lang/zh-cn'
// 统一导入el-icon图标，这个是把所有的图标都引入进来了
import * as Icons from '@element-plus/icons-vue'
import { getToken, cleanSession } from './utils/auth'
import resetForm from './utils/resetForm'
import objCoppy from './utils/objCoppy'
import myconfirm from './utils/myconfirm'
import { permission } from './directives/permission'
import * as echarts from 'echarts'
// 这里注意实例化的对象的引入方式也发生了变化，没有vue了
// 这里直接改成创建一个实例化对象app
const app = createApp(App);
app.use(store, key)
// 挂载路由
app.use(router)
app.directive('permission', permission)
app.use(ElementPlus, { locale })
// 这个最终的挂载要放在use等操作之后，否则会报错
// 所有的指令等都找不到实例化的对象
app.mount('#app')
// 这个是直接往实例对象上挂载新的属性的写法，取代了原有的vue.prototype.$myconfirm=...这种写法
app.config.globalProperties.$resetForm = resetForm
app.config.globalProperties.$objCoppy = objCoppy
app.config.globalProperties.$myconfirm = myconfirm
app.config.globalProperties.$echarts = echarts;
// 以组件的方式注册了字体图标，用来做动态生成
// console.log(Icons)
// 打印后可以得知这个Icons是全部的字体图标的组件集合
// typeof 和 keyof的作用
// typeof用来获取一个变量或者对象的类型
const person = {
  name: 'tom',
  age: 18
}
// 此时声明的类型就是模仿person类型推断出的一个类型
// typeof用来获取一个对象的类型，不要把这的typeof理解成js中的typeof
// ts中的typeof可以用来获取一个变量或者对象的类型
type tp = typeof person
// 同样keyof也是这个意思，不要理解为js中的keyof，
// 在这里keyof操作符是用来获取属性的名称的
// keyof作用：获取某一种类型的所有键（key），返回的额类型是一个联合类型
// 这时获取的这个keys是一个联合类型的类型name|age
type keys = keyof tp


Object.keys(Icons).forEach((key) => {
  // 全局组件的注册，第一个是组件的名字，第二个是组件的结构
  // 理想情况下应该是Icons[key] 但是这里不支持这种写法
  // 这里第二个参数的意义就是这个key是联合类型中的任何一个，至于是哪一个，需要后面传入
  // app.component(key, Icons[key as keyof typeof Icons])
  app.component(key, Icons[key]) // 4-29版本更新后，支持这种写法
});
// 新版不在支持
// const Icon = (props: { icon: string }) => {
//   const { icon } = props;
//   let a = typeof Icons
//   console.log(a); // object
//   // console.log(keyof(a)); // 报错 找不到名称keyof
//   // console.log(Icons[icon as keyof typeof Icons]);  // undefined
//   return createVNode(Icons[icon as keyof typeof Icons]);
// };
// app.component('Icon', Icon);

// 权限验证，全局路由守卫
const whiteList = ['/login']
router.beforeEach(async (to, from, next) => {
  let token = getToken()
  if (token) {
    if (to.path === 'login' || to.path === '/') {
      next({ path: '/' })
    } else {
      // 从vuex中获取菜单
      // 如果权限列表有数据，证明是已经登录过了（就是有token证明已经发送过登录请求了）
      // 同时还有permissions表明已经发送过获取用户信息和目录的请求，就可以直接进入下一页
      let hasRoles = store.state.user.permissions && store.state.user.permissions.length > 0
      if (hasRoles) {
        next()
      } else {
        // 如果没有这些信息，那就重发请求获取用户信息和目录信息
        try {
          await store.dispatch('user/getInfo')
          await store.dispatch('menu/getMenuList', router)
          // 确保动态添加的路由已经完全被加载上去
          // 解释https://blog.csdn.net/qq_41912398/article/details/109231418
          next({ ...to, replace: true })
        } catch (error) {
          //重置token
          console.log('获取用户信息以及目录列表报错了');
          cleanSession();
          //跳到登录
          next({ path: '/login' })
        }
      }
    }
  } else {
    // 没有token，判断去的路径如果在白名单，直接放行
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      // 如果不在白名单，直接跳回登录页面
      next({ path: '/login' })
    }
  }
})